package com.letoken.platform.app.controller;


import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.letoken.platform.app.service.AppBalanceService;
import com.letoken.platform.pub.constant.ConstantsValue;
import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.enums.BalanceTypeEnum;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.enums.LetokenTokenTypeEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.req.PageData;
import com.letoken.platform.pub.res.ApiResponse;
import com.letoken.platform.pub.util.LocalDateTool;
import com.letoken.platform.repo.app.dao.AppBalanceHistoryMapper;
import com.letoken.platform.repo.app.dao.AppBalanceWalletMapper;
import com.letoken.platform.repo.app.dao.AppUserWalletMapper;
import com.letoken.platform.repo.app.redis.RedisConfig;
import com.letoken.platform.repo.app.req.user.BalanceHistoryReq;
import com.letoken.platform.repo.app.req.user.BalanceTransferReq;
import com.letoken.platform.repo.app.res.user.AppBalanceHistoryRes;
import com.letoken.platform.repo.app.res.user.AppBalanceRes;
import com.letoken.platform.repo.mall.po.AppBalanceWallet;
import com.letoken.platform.repo.utlis.PageDataUtil;
import com.letoken.platform.repo.utlis.WebUtils;
import com.letoken.platform.repo.utlis.lock.Lock;
import com.letoken.platform.repo.utlis.lock.LockCategoryEnum;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/balance")
public class AppBalanceController {

    @Autowired
    AppBalanceWalletMapper appBalanceWalletMapper;
    @Autowired
    AppBalanceHistoryMapper appBalanceHistoryMapper;
    @Autowired
    AppUserWalletMapper appUserWalletMapper;
    @Autowired
    AppBalanceService appBalanceService;

    @Resource
    private RedisConfig redisConfig;


    @GetMapping("/query")
    public ApiResponse<AppBalanceRes> balanceQuery() {
        AppBalanceRes res = appBalanceService.createRecord();
        res.setRate(ConstantsValue.BALANCE_INCOME_RATE.multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.DOWN));
        if (!res.getIsNew()){
            LocalDateTime now = LocalDateTime.now();
            res.setYesterdayIncome(appBalanceHistoryMapper.incomeByTime(res.getUserId(), LocalDateTool.plusDay(now,0),LocalDateTool.plusDay(now,1),BalanceTypeEnum.INCOME.ordinal()));
        }
        res.setLetBalance(appUserWalletMapper.getAvailableBalance(LetokenTokenTypeEnum.LET.getId(), res.getUserId()));
        return ApiResponse.create(res);
    }



    private void checkout(BalanceTransferReq req){
        if ( req.getAmount().compareTo(BigDecimal.ONE) < 0){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_054);
        }
        Integer balanceId = appBalanceWalletMapper.getIdByUserId(LetokenTokenTypeEnum.LET.getId(),req.getUserId());
        Integer walletId = appUserWalletMapper.checkoutTokenIdAndUserId(LetokenTokenTypeEnum.LET.getId(), req.getUserId());
        if (ObjectUtils.isEmpty(balanceId) || ObjectUtils.isEmpty(walletId)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_051);
        }
        req.setBalanceId(balanceId);
        req.setWalletId(walletId);
    }


    @PostMapping("/transferIn")
    public ApiResponse transferIn(@Validated @RequestBody BalanceTransferReq req) {
        req.setUserId(WebUtils.getUserId());
        checkout(req);
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,req.getUserId());
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        try {
            rlock.lock(10, TimeUnit.SECONDS);
            appBalanceService.transferIn(req);
        }finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }
        return ApiResponse.create();
    }


    @PostMapping("/transferOut")
    public ApiResponse transferOut(@Validated @RequestBody BalanceTransferReq req) {
        req.setUserId(WebUtils.getUserId());
        checkout(req);
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,req.getUserId());
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        try {
            rlock.lock(10, TimeUnit.SECONDS);
            appBalanceService.transferOut(req);
        }finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }

        return ApiResponse.create();
    }


    @PostMapping(value = "/history")
    public ApiResponse<PageData<AppBalanceHistoryRes>> history(@Validated @RequestBody BalanceHistoryReq req ) {
        Integer userId = WebUtils.getUserId();
        req.setUserId(userId);
        if (StringUtils.isNotBlank(req.getDate())){
            req.setStartTime(LocalDateTool.strToLocalDate(LocalDateTool.getByMonth(req.getDate(),1))) ;
            req.setEndTime(LocalDateTool.strToLocalDate(LocalDateTool.getByMonth(req.getDate(),-1)));
        }
        if (ObjectUtils.isNotEmpty(req.getType())){
            if (req.getType() < 0){
                req.setType(null);
            }
        }

        Page<AppBalanceHistoryRes> page = new Page<>(req.getPageReq().getPage(), req.getPageReq().getSize());
        Page<AppBalanceHistoryRes> detailPage = appBalanceHistoryMapper.history(page,req);
        if (!CollectionUtils.isEmpty(detailPage.getRecords())) {
            detailPage.getRecords().forEach(x -> {
                x.setTypeDisplay(BalanceTypeEnum.getDesc(x.getType()));
            });
        }
        return ApiResponse.create(PageDataUtil.getPageData(detailPage));
    }












}
